/**
 *
 * @author ilya portnyagin iportnyagin@gmail.com
 */

package ru.portnyagin.helpdeskru.service;

import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import ru.portnyagin.helpdeskru.model.HistoryRequest;
import ru.portnyagin.helpdeskru.model.Organization;
import ru.portnyagin.helpdeskru.model.Request;
import ru.portnyagin.helpdeskru.model.Request_;
import ru.portnyagin.helpdeskru.model.ServiceObject;
import ru.portnyagin.helpdeskru.model.StateRequest;
import ru.portnyagin.helpdeskru.model.UserHD;
import ru.portnyagin.helpdeskru.util.JsfUtil;

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class RequestService extends AbstractService<Request> {
    
    @PersistenceContext(unitName = "HelpDeskRuPU")
    private EntityManager em;

    @EJB
    private ru.portnyagin.helpdeskru.service.UserHDService userHDService;
        
    protected EntityManager getEntityManager() {
        return em;
    }
    
    @Resource
    private UserTransaction ut;

    public RequestService() {
        super(Request.class);
    }

    // OPENED INPROGRESS HOLDED NEEDINFO REOPENED DONE CLOSED
    
    

    // for CustomerController
    public List<Request> findByStateAndCustomer(List<StateRequest> listStateRequest, UserHD createdBy) {
        
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery();
        Root<Request> z = cq.from(Request.class);
            
        Predicate whereState = null;
        
        if(listStateRequest != null && !listStateRequest.isEmpty()) {
            for(StateRequest st: listStateRequest) {
                if(whereState == null) {
                    whereState = cb.equal(z.get(Request_.stateRequest), st);
                } else {
                    whereState = cb.or(whereState, cb.equal(z.get(Request_.stateRequest), st));
                }
            }
        } 
        
        Predicate whereCreatedBy = null;
        Predicate whereCreatedByOrg = null;
        
        if(createdBy != null) {
            
            whereCreatedBy = cb.equal(z.get(Request_.createdBy), createdBy);
            
            if(createdBy.getOrganization() != null) {
                List<UserHD> listUsers = userHDService.findUsersByOrganization(createdBy.getOrganization());
                for(UserHD us: listUsers) {
                    if(whereCreatedByOrg == null) {
                        whereCreatedByOrg = cb.equal(z.get(Request_.createdBy), us);
                    } else {
                        whereCreatedByOrg = cb.or(whereCreatedByOrg, cb.equal(z.get(Request_.createdBy), us));
                    }
                }
            }        
        }
        
        
        
//        cq.where(cb.and(whereState, cb.or(whereCreatedBy, whereCreatedByOrg))); // у заказчика не задана орг-ия - whereCreatedByOrg - NPE

        if(whereCreatedByOrg == null) {
            cq.where(cb.and(whereState, whereCreatedBy));
        } else {
            cq.where(cb.and(whereState, cb.or(whereCreatedBy, whereCreatedByOrg)));
        }
        
        
        cq.orderBy(cb.desc(z.get(Request_.id)));
        cq.select(z);

        return getEntityManager().createQuery(cq).getResultList();
    }        
    
    
    
    
    

    // for ServiceController
    public List<Request> findByStateAndPerfomer(List<StateRequest> listStateRequest, UserHD perfomer, 
            boolean showRequestOnlyForCurrentUser) {
        
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery();
        Root<Request> z = cq.from(Request.class);
            
        Predicate whereState = null;
        
        if(listStateRequest != null && !listStateRequest.isEmpty()) {
            for(StateRequest st: listStateRequest) {
                if(whereState == null) {
                    whereState = cb.equal(z.get(Request_.stateRequest), st);
                } else {
                    whereState = cb.or(whereState, cb.equal(z.get(Request_.stateRequest), st));
                }
            }
        } 
        
        Predicate wherePerfomer = null;
        
        if(perfomer != null && showRequestOnlyForCurrentUser) {
            wherePerfomer = cb.equal(z.get(Request_.performer), perfomer);
        }
        
        
        
        if(wherePerfomer == null) {
            cq.where(whereState);
        } else {
            cq.where(cb.and(whereState, wherePerfomer));
        }

        cq.orderBy(cb.desc(z.get(Request_.id)));
        cq.select(z);

        return getEntityManager().createQuery(cq).getResultList();
    }        
    
    
    
    
    
    
    
    
    
    // for CurrentRequestBean
    public List<Request> findByServiceObject(Request current) { // история заявок по объекту обслуживания
        
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery();
        Root<Request> z = cq.from(Request.class);
        
        cq.where(cb.and(
                    cb.isNotNull(z.get(Request_.serviceObject)),
                    cb.equal(z.get(Request_.serviceObject), current.getServiceObject()))
                );
                
        cq.orderBy(cb.desc(z.get(Request_.id)));
        cq.select(z);

        return getEntityManager().createQuery(cq).getResultList();
    }
    
    
    // for customer and service search
    public List<Request> findByVariousCriteria(Long id, 
            Organization organization,
            UserHD createdBy, 
            Date createdDateBegin, 
            Date createdDateEnd, 
            String description,
            String text, 
            ServiceObject serviceObject, 
            StateRequest stateRequest, 
            Date stateDateBegin, 
            Date stateDateEnd,
            UserHD perfomer) {
        
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery();
        Root<Request> z = cq.from(Request.class);
        
        Predicate where = cb.conjunction();
        Predicate whereCreatedByOrg = null;
                
        if(id != null) {
            where = cb.and(where, cb.equal(z.get(Request_.id), id));
        }
        
        if(organization != null) {
            List<UserHD> listUsers = userHDService.findUsersByOrganization(organization);
            for(UserHD us: listUsers) {
                if(whereCreatedByOrg == null) {
                    whereCreatedByOrg = cb.equal(z.get(Request_.createdBy), us);
                } else {
                    whereCreatedByOrg = cb.or(whereCreatedByOrg, cb.equal(z.get(Request_.createdBy), us));
                }
            }
            if(whereCreatedByOrg == null) { // это может быть если в орг-ии нет пользователей - результат поиска пустой
                whereCreatedByOrg = cb.isNull(z.get(Request_.createdBy));
            }
        }
        
        if(createdBy != null) {
            where = cb.and(where, cb.equal(z.get(Request_.createdBy), createdBy));
        }
        
        if(createdDateBegin != null && createdDateEnd != null) {
            where = cb.and(where, cb.between(z.get(Request_.createdDate), createdDateBegin, createdDateEnd));
        }
        
        if(description != null) {
            where = cb.and(where, cb.like(z.get(Request_.description), "%" + description + "%"));
        }
        
        if(text != null) {
            where = cb.and(where, cb.like(z.get(Request_.text), "%" + text + "%"));
        }
        
        if(serviceObject != null) {
            where = cb.and(where, cb.equal(z.get(Request_.serviceObject), serviceObject));
        }
        
        if(stateRequest != null) {
            where = cb.and(where, cb.equal(z.get(Request_.stateRequest), stateRequest));
        }
        
        if(stateDateBegin != null && stateDateEnd != null) {
            where = cb.and(where, cb.between(z.get(Request_.stateDate), stateDateBegin, stateDateEnd));
        }
    
        if(perfomer != null) {
            where = cb.and(where, cb.equal(z.get(Request_.performer), perfomer));
        }
              
        
//        cq.where(cb.and(where, whereCreatedByOrg));   при whereCreatedByOrg == null - NPE  
        
        if(whereCreatedByOrg == null) { // whereCreatedByOrg инициализируется только когда задана орг-ия
            cq.where(where);     
        } else {
            cq.where(cb.and(where, whereCreatedByOrg));     
        }
   
            
        cq.orderBy(cb.desc(z.get(Request_.id)));
        cq.select(z);

        return getEntityManager().createQuery(cq).getResultList();
    }
    
    
    
    
    
    
    
    
    
    
    
    public void update(Request current, UserHD curUser) {
        
        Request preUpdate;
        if (current.getId() == null) {     // новый запрос
            preUpdate = new Request();
        }
        else {
            preUpdate = em.find(Request.class, current.getId());
            if (preUpdate == null) {
                JsfUtil.addErrorMessage(current.getId() + ": "+ ResourceBundle.getBundle("property",
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("requestNotFound"));            
                
                return;
            }
        }
        
        try {
            try {
                ut.begin();
            } catch (javax.transaction.NotSupportedException | SystemException ex) {
//                ex.printStackTrace();
                JsfUtil.addErrorMessage(ex.toString());
            }
            
            // состояние
            if(!current.getStateRequest().equals(preUpdate.getStateRequest())) {
                current.setStateDate(new Date()); // при изменении состояния обновляем дату состояния
                HistoryRequest h = new HistoryRequest(current, curUser, new Date(), 
                        ResourceBundle.getBundle("property", 
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("common.stateRequest"),
                        current.getStateRequest().getName());
                em.persist(h);
            }

            // исполнитель. м.б. null
            String curPerformer;
            String prevPerformer;
            if(current.getPerformer() != null) { 
                curPerformer = current.getPerformer().getName();
            }
            else {
                curPerformer = "";
            }
            
            if(preUpdate.getPerformer() != null) { 
                prevPerformer = preUpdate.getPerformer().getName();
            }
            else {
                prevPerformer = "";
            }
            
            if(!curPerformer.equals(prevPerformer)) {
                    HistoryRequest h = new HistoryRequest(current, curUser, new Date(), 
                        ResourceBundle.getBundle("property", 
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("common.performer"),
                        curPerformer);
                    em.persist(h);
            }

            // объект обслуживания. м.б. null
            String curServiceObject;
            String prevServiceObject;
            if(current.getServiceObject() != null) { 
                curServiceObject = current.getServiceObject().getName();
            }
            else {
                curServiceObject = "";
            }
            
            if(preUpdate.getServiceObject() != null) { 
                prevServiceObject = preUpdate.getServiceObject().getName();
            }
            else {
                prevServiceObject = "";
            }
            
            if(!curServiceObject.equals(prevServiceObject)) {
                    HistoryRequest h = new HistoryRequest(current, curUser, new Date(), 
                        ResourceBundle.getBundle("property", 
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("common.serviceObject"),
                        curServiceObject);
                    em.persist(h);
            }
            
            // описание м.б. null
            String curDescription;
            String prevDescription;
            if(current.getDescription() != null) {
                curDescription = current.getDescription();
            }
            else {
                curDescription = "";
            }
            
            if(preUpdate.getDescription() != null) {
                prevDescription = preUpdate.getDescription();
            }
            else {
                prevDescription = "";
            }
            
            if(!curDescription.equals(prevDescription)) {
                    HistoryRequest h = new HistoryRequest(current, curUser, new Date(), 
                        ResourceBundle.getBundle("property", 
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("common.description"),
                        curDescription);
                    em.persist(h);
            }
            
            // приоритет
            if(!current.getPriority().equals(preUpdate.getPriority())) {
                HistoryRequest h = new HistoryRequest(current, curUser, new Date(), 
                        ResourceBundle.getBundle("property", 
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("common.priority"),
                        current.getPriority().getName());
                em.persist(h);
            }
            
            
            if(current.getId() != null) {
                em.merge(current);
            } 
            else {
                em.persist(current);
            } 
            
            try {
                ut.commit();
            } catch (javax.transaction.RollbackException | HeuristicMixedException | 
                    HeuristicRollbackException | SecurityException | IllegalStateException | 
                    SystemException ex) {
                JsfUtil.addErrorMessage(ex.toString());
            }
            
            JsfUtil.addSuccessMessage(current.getId() + ": "+ ResourceBundle.getBundle("property",
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("requestSaved"));            

        } catch (OptimisticLockException e) {
            
            try {
                ut.rollback();
            } catch (    IllegalStateException | SecurityException | SystemException ex) {
//                ex.printStackTrace();
                JsfUtil.addErrorMessage(ex.toString());
            }
            
            JsfUtil.addErrorMessage(current.getId() + ": " + ResourceBundle.getBundle("property",
                        FacesContext.getCurrentInstance().getViewRoot().getLocale()).
                        getString("requestSaveErrorOptimistic"));            
        
            
        }
    }    
    

}
